#include "GameWorld.h"
#include "General Utils.hpp"
#include "Globals.hpp"
#include "EventHandler.h"
#include "GUIManager.h"
#include "GameBoard.h"
#include "BaseGameEntity.h"
#include "Vector2D.h"
#include "GameBoard.h"
#include "Defines.hpp"
#include "PathPlanner.h"


GameWorld::GameWorld()
{
	m_SelectionBox = InvalidRect;
}


void GameWorld::Initialize()
{
	m_pPathManager = new PathManager<PathPlanner>(40);

	// Set the camera's clipping plane up
	m_Camera.SetClippingPlane(&m_ClipPlane);

	// Initialize the chatbox
	Dimension chatboxDim    = {400, 170};
    Coords chatboxLoc		= Coords(10, DataLoader->WINDOW_HEIGHT - chatboxDim.h - 150);


	m_Chatbox.SetBackgroundColor(Key->GetColor("100, 100, 100, 100"));
	m_Chatbox.SetDimensions(chatboxDim);
	m_Chatbox.SetFont("onuava small");
	m_Chatbox.SetFontColor(Key->GetColor("255, 200, 150"));
	m_Chatbox.SetLocation(chatboxLoc);
	m_Chatbox.SetLogSize(20);
	m_Chatbox.SetMaximumInputSize(100);


	// Sound GUI
	Coords loc;
	loc.x = 50;
	loc.y = 50;
	m_SoundGUI.Init("Fonts/Self_Destruct.otf", loc);

	m_SoundGUI.AddSound("Losing Touch", "LosingTouch");
	m_SoundGUI.AddSound("Sandstorm", "Sandstorm");
	m_SoundGUI.AddSound("Riot", "Riot");

	// Overlay Menu
	m_OverlayMenu.RecreateButtonLocations(4);
	m_OverlayMenu.SetButtonMessage("Menu", 0);
	m_OverlayMenu.SetButtonMessage("Exit", 1);
	m_OverlayMenu.SetButtonMessage("Show Edges", 2);
	m_OverlayMenu.SetButtonMessage("Sound GUI", 3);

}

///////////////// ====================== RENDER FUNCTIONS ====================== \\\\\\\\\\\\\\\\
// Functions that render stuff

// ============================Render=============================
// Draws the map and the all the game entities in the entity list
// of GameWorld
void GameWorld::Render()
{

	// Render the map
	m_pGameMap->RenderGrid();
	if(m_bRenderCells)
		m_pGameMap->GetPartition().RenderCells();


	GetNavGraph()->DisplayEdges(&m_ClipPlane, World->m_pGameMap->MapWidth());

	// Render GameEntities
	RenderGameEntities();


	// Render the selection box if it should be rendered
	RenderSelectionBox();

	// Chatbox
	m_Chatbox.Render();

	// Overlay. Don't ask me why they're 2 functions
	m_OverlayMenu.DrawTrans();
	m_OverlayMenu.DrawOpaq();

	// SoundGUI. Same as above
	m_SoundGUI.DrawTrans();
	m_SoundGUI.DrawOpaq();

	
	m_PoolManager.RenderPools();

	// Minimap
	m_pGameMap->RenderMinimap();

	// Blah stuff. 
	Key->GetFont("Onuava")->drawText(500.0, 50.0, RectToString(m_SelectionBox).c_str(), green); 
	GameRectangle FixedRect = GetRect(m_SelectionBox.x, m_SelectionBox.y, 
									m_SelectionBox.x + m_SelectionBox.width, 
									m_SelectionBox.y + m_SelectionBox.height);

	Key->GetFont("Onuava")->drawText(500.0, 100.0, RectToString(FixedRect).c_str(), red);


	int mouseX, mouseY;
	SDL_GetMouseState(&mouseX, &mouseY);
	mouseX += ClipPlane->GetX(); mouseY += ClipPlane->GetY();
	Vector2D pos = WorldMap->GetGraph()->GetNode(WorldMap->GetGraph()->GetClosestNodeToPosition(Vector2D(mouseX,mouseY))).Pos();
	
	DrawPrimitives->DrawRectangle(pos.x - ClipPlane->GetX(), pos.y - ClipPlane->GetY(), pos.x + 32 - ClipPlane->GetX(), pos.y+32 - ClipPlane->GetY(), white);
}

// =======================Render Selection Box ====================
// Renders the selection box. Makes sure that the selection box is
// valid first. (The selection box is purposefully set to an invalid
// value once the player releases his mouse button)

void GameWorld::RenderSelectionBox()
{
	if( m_SelectionBox != InvalidRect )
	{
		DrawPrimitives->DrawRectangle(m_SelectionBox.x - ClipPlane->GetX(), m_SelectionBox.y - ClipPlane->GetY(),
									m_SelectionBox.x + m_SelectionBox.width - ClipPlane->GetX(),
									m_SelectionBox.y + m_SelectionBox.height - ClipPlane->GetY(), blue);
	}
}

// ======================== RenderGameEntities ======================
// Calls the render function of each entity in m_EntityList

void GameWorld::RenderGameEntities()
{
	// Render each game entity
	EntityList::iterator gameEntity = m_Entities.begin();

	while(gameEntity != m_Entities.end())
	{
		(*gameEntity)->Render();
		++gameEntity;
	}

}


//////////////// ======= I DON'T KNOW WHAT TO LABEL THIS SECTION ====== \\\\\\\\\\\\\\\\\\\\
// I haven't gotten to organizing these yet 

// ====================FreeMemory=========================
// Deletes the memory that needs to be freed come the end
// of the game

void GameWorld::FreeMemory()
{
	NullDelete(m_pGameMap);

}

// ==================LoadMap=======================
void GameWorld::LoadMap(std::string filename)
{
	m_pGameMap = new GameBoard(filename);
}

// =============== ToggleSoundGUI ===================
// Flips the active state of the sound GUI.
void GameWorld::ToggleSoundGUI()
{
	m_SoundGUI.SetActive(!m_SoundGUI.GetActive());
}

// ====================AddEntity===================
// The entity must be added in order for it to be
// displayed in the game screen, and for the 
// cell space partition to work correctly

void GameWorld::AddEntity(BaseGameEntity* anEntity)
{
	m_pSctEnt = anEntity;
	m_Entities.push_back(anEntity);
	m_pGameMap->AddEntity(anEntity);

	if(anEntity->EntityType() >= 100 && anEntity->EntityType() < 200)
		m_Characters.push_back((Character*)anEntity);
}


///////////////// =================== UPDATE FUNCTIONS ====================== \\\\\\\\\\\\\\\\\\\


// ========================Update========================
// Updates the keystates and calls various update functions
// of the things that make up GameWorld

void GameWorld::Update(double time_elapsed )
{
	m_pPathManager->UpdateSearches();

	// Update keystates
	m_Keystates = SDL_GetKeyState( NULL );
	
	// Selection Box & GameMousePosition
	UpdateSelectionBox();
	UpdateGameMousePosition();

	// Camera
	m_Camera.Update(m_ScreenMousePos, m_Keystates);

	// GUI
	GUI->Update();
	UpdateEntityPanel();
	m_OverlayMenu.Update();
	m_SoundGUI.Update();


	// Entities
	UpdateGameEntities(time_elapsed);

}

// ======================== UpdateGameEntities ======================
// Calls the Update function of each entity on the m_Entities list
void GameWorld::UpdateGameEntities(double time_elapsed)
{
	EntityList::iterator GameEntity;

	for(GameEntity = m_Entities.begin(); GameEntity != m_Entities.end(); ++GameEntity)
	{
		(*GameEntity)->Update(time_elapsed);
	}
}

// ====================== UpdateEntityPanel =================================
// Updates the GUI panel that contains buttons to select any game entity.
// Merely clears the old one and loops through all the game entities to
// find the ones that are selected

void GameWorld::UpdateEntityPanel()
{
	GUI->RegisterPanel(Key->GetPanelIDs()->Game_Unit);
	GUI->ClearButtons(); // Don't add on top of others. 

	EntityList::iterator curEntity = m_Entities.begin();

	while(curEntity != m_Entities.end())
	{
		if( (*curEntity)->Selected() )
		{
			GUI->AddButton( (*curEntity)->ID(), "b_Game_Dragon");
			GUI->SetButtonShading(true, (*curEntity)->ID());
		}
		curEntity++;
	}

	if(GUI->NumButtons() == 0)
		GUI->DeactivePanel();
	else
		GUI->ActivatePanel();
}


// ================= UpdateMousePosition ======================
// Updates the ScreenMousePos SDL_MOUSEMOTION events

void GameWorld::UpdateMousePosition(SDL_Event& event)
{
	// Strictly speaking, this should only be called
	// WITH a mouse motion event. But it can't hurt to
	// double check
	if(event.type == SDL_MOUSEMOTION)
	{
		m_ScreenMousePos.x = event.motion.x;
		m_ScreenMousePos.y = event.motion.y;
	}
}

// ======================= UpdateMinimapEntity ========================
// Updates the specified entity on the minimap.  This readjusts the pixel location
// of that entity on the minimap
void GameWorld::UpdateMinimapEntity(int ID, const Vector2D& position)
{
	m_pGameMap->UpdateMinimapEntity(ID, position);
}

// ======================= UpdateGameMousePosition ========================
// The game mouse position can also be affected by use of arrow keys
// even if the mouse is not moved. So, it should be updated regardless
// of a move in the mouse

void GameWorld::UpdateGameMousePosition()
{
	m_GameMousePos.x = ClipPlane->GetX() + m_ScreenMousePos.x;
	m_GameMousePos.y = ClipPlane->GetY() + m_ScreenMousePos.y;
}

// ====================== UpdateSelectionBox =======================
// Updates the selection box based on the movements made by the mouse
// The selection box is not updated updated when it is set to InvalidRect
// as is the case when the mouse button is not held down.

void GameWorld::UpdateSelectionBox()
{
	if(m_SelectionBox != InvalidRect)
	{
		m_SelectionBox.width = (m_GameMousePos.x) - m_SelectionBox.x;
		m_SelectionBox.height = (m_GameMousePos.y) - m_SelectionBox.y;


		// If an arrow key was pressed it should also be updated
		if(m_Keystates[SDLK_UP] || m_Keystates[SDLK_LEFT] || m_Keystates[SDLK_RIGHT] || m_Keystates[SDLK_DOWN])
		{
			m_SelectionBox.width = (m_GameMousePos.x) - m_SelectionBox.x;
			m_SelectionBox.height = (m_GameMousePos.y) - m_SelectionBox.y;
		}
	}
	

}

/////////////////////////////////// General Utils \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

// ==================== EnforceNonPenentration ===========================
// Keeps the given character from overlapping with any other characters
// on the map.
// Also, stop laughing.
void GameWorld::EnforceNonPenetration(BaseGameEntity* entity)
{
	
	WorldMap->GetPartition().CalculateOverlaps(entity);
	BaseGameEntity* curEntity = WorldMap->GetPartition().begin();

	while(curEntity != 0)
	{
		if(curEntity->EntityType() >= 200 &&  curEntity->EntityType() < 300)
		{
			curEntity = WorldMap->GetPartition().next();
			continue;
		}

		if(curEntity->ID() == entity->ID())
		{
			curEntity = WorldMap->GetPartition().next();
			continue;
		}

		Vector2D ToEntity = entity->Pos() - curEntity->Pos();
		double DistFromEachOther = ToEntity.Length();
		
		double AmountOfOverLap = entity->BRadius() + curEntity->BRadius() - 
									DistFromEachOther;
		if(AmountOfOverLap > 0)
		{
			Vector2D OldPos = entity->Pos();

			if(DistFromEachOther != 0)
			{
				if( !(entity->EntityType() >= 200 && entity->EntityType() < 300)) // DO NOT MOVE BUILDINGS
					entity->SetPos(entity->Pos() + (ToEntity/DistFromEachOther)*AmountOfOverLap);
			}
			else
				if(!(entity->EntityType() >= 200 && entity->EntityType() < 300))
					entity->SetPos(entity->Pos() + Vector2D(AmountOfOverLap, 0.0));

			WorldMap->GetPartition().UpdateEntity(entity, OldPos);

		}

		curEntity = WorldMap->GetPartition().next();
	}
}

// ====================== IsPathObstructed ==========================
// Returns whether or not an entity with the given bound radius
// can create a path between point A to point B

bool GameWorld::IsPathObstructed(Vector2D A, Vector2D B, double BoundRadius)
{

	Vector2D ToB = Vec2DNormalize(B-A);

	 Vector2D curPos = A;

	 while (Vec2DDistanceSq(curPos, B) > BoundRadius*BoundRadius)
	 {   
		//advance curPos one step
		curPos += ToB * 0.5 * BoundRadius;
    
		//test all builds against the new position
		m_pGameMap->GetPartition().CalculateOverlaps(curPos, BoundRadius);
		BaseGameEntity* curEntity = WorldMap->GetPartition().begin();

		while(curEntity != 0)
		{
			if(curEntity->IsBuilding())
				return true;

			curEntity = WorldMap->GetPartition().next();
		}

		// Now check for water. 
		if(WorldMap->GetPartition().CollidesWithWater(curPos, BoundRadius))
			return true;
	
	}

	return false;

}

/////////////////////// ============== Get Functions ============ \\\\\\\\\\\\\\\\\\\\\
// A couple useful get functions that can be used...

ClippingPlane*	GameWorld::GetClipPlane() {return &m_ClipPlane;}
GameBoard*		GameWorld::GetMap() {return m_pGameMap;}
NavGraph*		GameWorld::GetNavGraph() {return m_pGameMap->GetGraph();}

Point			GameWorld::ScreenMousePos() {return m_ScreenMousePos;}
Point			GameWorld::GameMousePos() {return m_GameMousePos;}
Uint8*			GameWorld::GetKeystates() {return m_Keystates;}

EntityList		GameWorld::Entities() {return m_Entities;}
GameRectangle	GameWorld::SelectionBox() {return m_SelectionBox;}
PoolManager&	GameWorld::GetPoolManager(){return m_PoolManager;}